package cn.online.taxi.api.listen.controller;

import cn.online.taxi.common.constant.RedisKeyConstant;
import cn.online.taxi.common.jedis.JedisClient;
import cn.online.taxi.common.utils.LatNLongDistance;
import cn.online.taxi.common.utils.SpringUtil;
import cn.online.taxi.common.utils.StringUtil;
import cn.online.taxi.manager.dto.front.DriverReceiving;
import cn.online.taxi.manager.dto.front.Order;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;


@ServerEndpoint(value = "/websocket/{token}")
@Component
public class WebSocketController {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketController.class);

    public static final Map<String, Session> sessionMap = new ConcurrentHashMap();

    private static int onlineCount = 0;
    private Session session;
    private String token;

    private static ApplicationContext applicationContext;
    public static void setApplicationContext(ApplicationContext context){
        applicationContext = context;
    }

    private JedisClient jedisClient;

    @OnOpen
    public void onOpen(@PathParam("token") String token, Session session) {
        this.token = token;
        this.session = session;

        addOnlineCount();
        sessionMap.put(token, session);
        logger.info(this.token + " 已连接");
    }

    @OnClose
    public void onClose() {
        sessionMap.remove(token);
        logger.info(this.token + " 已关闭");
        subOnlineCount();
    }

    /**
     * 乘客，司机实时分享位置
     * @param message
     * @throws IOException
     */
    @OnMessage
    public void onMessage(String message) throws IOException {
//        logger.info("收到消息：" + message);
        JSONObject jsonTo = JSONObject.parseObject(message);
        Object pong = jsonTo.get("pong");
        Object identity = jsonTo.get("identity");
        Object token = jsonTo.get("token");
        Object userLongitude = jsonTo.get("userLongitude");
        Object userLatitude = jsonTo.get("userLatitude");
        if (pong != null) {//pong数据不做处理
            // 实时更新用户位置
            // 如果是乘客则不做处理
            if (identity.equals("passenger")) {
                return;
            }
            // 如果是司机则更新实时位置
            jedisClient = applicationContext.getBean(JedisClient.class);
            List<String> jsonList = jedisClient.hvals(RedisKeyConstant.DRIVER_LISTEN_ORDER_PRE);
            for (String json : jsonList) {
                DriverReceiving driverReceiving = new Gson().fromJson(json, DriverReceiving.class);
                if (token.equals(driverReceiving.getToken())) {
                    driverReceiving.setDriverLongitude((String) userLongitude);
                    driverReceiving.setDriverLatitude((String) userLatitude);
                    // 重新写入redis
                    jedisClient.hset(RedisKeyConstant.DRIVER_LISTEN_ORDER_PRE, String.valueOf(driverReceiving.getDriverId()), new Gson().toJson(driverReceiving));
                    break;
                }
            }
        }
        if(identity != null && identity.equals("driver")) {
            if (pong != null) {
                return;
            }
            sendMessageTo(jsonTo.get("msg").toString(), jsonTo.get("to").toString(), message);
        }
        if (!jsonTo.get("to").equals("All")) {
            sendMessageTo(jsonTo.get("msg").toString(), jsonTo.get("to").toString(), message);
        } else {
            sendMessageAll(jsonTo.get("msg").toString());
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    public void sendMessageTo(String message, String toUser, String jsonMessage) throws IOException {
        boolean sendFlag = false;
        jedisClient = applicationContext.getBean(JedisClient.class);
        String orderNumber = message;
        // 根据订单号, 在redis里查询出订单
        String orderJSON = jedisClient.get(RedisKeyConstant.ORDER_KEY_PRE + ":" + orderNumber);
        Order order = new Gson().fromJson(orderJSON, Order.class);
        JSONObject jsonTo = JSONObject.parseObject(jsonMessage);
        // 用户身份
        Object identity = jsonTo.get("identity");
        // 用户实时经纬度
        double longitude = Double.parseDouble((String)jsonTo.get("userLongitude"));
        double latitude = Double.parseDouble((String)jsonTo.get("userLatitude"));
        if (identity.equals("driver")) {
            // 订单在去接乘客的状态下，检测司机若到达指定位置的50米内，自动将该订单的状态更为3（司机到达上车点）
            if (order.getStatus() == 2 ) {
                double startLongitude = Double.parseDouble(order.getStartLongitude());      // 目标位置经度
                double startLatitude = Double.parseDouble(order.getStartLatitude());        // 目标位置纬度
                double distance = LatNLongDistance.distanceByLongNLat(startLongitude, startLatitude, longitude, latitude);
                // 如果司机位置在乘客下单位置50米内, 则判断为司机到达下单位置
                if (distance < 50) {
                    // 司机到达乘客起点, 修改订单状态
                    order.setDriverStatus(3);
                    order.setDriverArrivedTime(new Date());
                    order.setStatus(4);
                    // 写入redis
                    jedisClient.set(RedisKeyConstant.ORDER_KEY_PRE + ":" + orderNumber, new Gson().toJson(order));
                }
            }
            // 订单在开始行程状态下，检测司机若到达目标地址的50米内，自动将该订单状态改为6（到达目的地，行程结束）
            if (order.getStatus() == 6) {
                double startLongitude = Double.parseDouble(order.getEndLongitude());      // 目标位置经度
                double startLatitude = Double.parseDouble(order.getEndLatitude());        // 目标位置纬度
                double distance = LatNLongDistance.distanceByLongNLat(startLongitude, startLatitude, longitude, latitude);
                // 如果司机位置在乘客下单终点位置50米内，则判断为司机到达终点位置
                if (distance < 50) {
                    // 司机到达终点位置, 修改订单状态
                    order.setPassengerGetoffTime(new Date());
                    order.setPassengerGetoffLongitude(String.valueOf(longitude));
                    order.setPassengerGetoffLatitude(String.valueOf(latitude));
                    order.setDriverStatus(5);
                    order.setStatus(6);
                    order.setIsPaid(0);
                    // 写入redis
                    jedisClient.set(RedisKeyConstant.ORDER_KEY_PRE + ":" + orderNumber, new Gson().toJson(order));
                }
            }
        } else if (identity.equals("passenger")) {

        }
        Set<Map.Entry<String, Session>> entries = sessionMap.entrySet();
        for (Map.Entry<String, Session> entry : entries) {
            String key = entry.getKey();
            if (StringUtil.isNotBlank(key) && key.equals(toUser)) {
                Session session = entry.getValue();
                if (session != null) {
                    System.out.println("发送消息给：" + session);
                    JSONObject msgData = new JSONObject();
                    msgData.put("msg", message);
                    session.getAsyncRemote().sendText(msgData.toString());
                    sendFlag = true;
                }
            }

        }

        if (!sendFlag) {
            logger.info("收到用户消息，目标接受者不在本服务器，发送redis广播");
            StringRedisTemplate stringRedisTemplate = SpringUtil.getBean(StringRedisTemplate.class);
            stringRedisTemplate.convertAndSend("chat", jsonMessage);
        }
    }

    public void sendMessageAll(String message) {
//        Collection<Session> values = sessionMap.values();
//        for (Session session : values) {
//            JSONObject msgData = new JSONObject();
//            msgData.put("msg", message);
//            session.getAsyncRemote().sendText(msgData.toString());
//        }
        // 查询出所有已上线的司机
        jedisClient = applicationContext.getBean(JedisClient.class);
        List<String> jsonList = jedisClient.hvals(RedisKeyConstant.DRIVER_LISTEN_ORDER_PRE);
        List<DriverReceiving> driverReceivingList = new ArrayList<DriverReceiving>();
        for (String json : jsonList) {
            DriverReceiving driverReceiving = new Gson().fromJson(json, DriverReceiving.class);
            driverReceivingList.add(driverReceiving);
        }
        //在sessionMap里查找出与redis里相对应的司机
        for (Map.Entry<String, Session> entry : sessionMap.entrySet()) {
            Order order = JSON.parseObject(message, Order.class);
            // 筛选符合条件的司机
            for (DriverReceiving driver : driverReceivingList) {
                if (entry.getKey().equals(driver.getToken())) {
                    // 以乘客为中心，半径为5公里，查询在此范围内的司机
                    double range = 5000;
                    double userLongitude = Double.parseDouble(order.getUserLongitude());      // 用户位置经度
                    double userLatitude = Double.parseDouble(order.getUserLatitude());        // 用户位置纬度
                    double driverLongitude = Double.parseDouble(driver.getDriverLongitude()); // 司机位置经度
                    double driverLatitude = Double.parseDouble(driver.getDriverLatitude());   // 司机位置纬度
                    double distance = LatNLongDistance.distanceByLongNLat(userLongitude, userLatitude, driverLongitude, driverLatitude);
                    if (distance < range) {
                        Session session = entry.getValue();
                        JSONObject msgData = new JSONObject();
                        msgData.put("msg", order);
                        session.getAsyncRemote().sendText(msgData.toString());
                    }
                }
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        onlineCount--;
    }

    public static synchronized Map<String, Session> getClients() {
        return sessionMap;
    }
}
